extends Chara

var utils = null
#角色详情窗口路径，用于mod自定义角色弹窗
# var dlgPath = "res://tscn/charaDlg/charaDlg.tscn"
var dlg = preload("res://!ve/base/tscn/charaDlg/charaDlg.tscn")
#角色额外技能池，存放技能ID，角色随机抽取技能时将包括这些技能
var specSkills = []

#角色立绘资源，需要提前load好
var pic


#静态立绘路径
var staticPic = null
#角色羁绊id
var trait = ""
#角色羁绊数量
var traitcnt = []
#角色沉默时间
var silenceTime = 0
#角色击杀数
var killNum = 0
#投射物飞行速度
var projectileSpd = 600
#忽略学习技能的等级配置，若为：[2,3]则升到2和3级时不会弹出技能学习窗口
var skipSkillLvs = []
#由于原版根据主职业roll随机装备的函数只支持默认的几大主职业，对没有那些主职业的MOD角色不友好
#所以添加此属性，用于指定角色的装备主职业，指定后该角色会从这个tab随机抽装备
var exTab = ""
#主职业索引
var mainTabIndex = 0
#召唤物的召唤者
var summor = null
#最大召唤物数量
var maxSumm = 10
#护盾上限增益值，百分比
var maxWardBouns = 1.0

var attEx = Att.new()

#带atkInfo的普攻信号
signal onNormalAtk(atkInfo)
#受到致命伤害之前
signal beforeDeathDmg(atkInfo)
#实际受到伤害之前
signal beforeHurt(atkInfo)
func _init():
	._init()
	utils = sys.get_node("VEBase").utils
	if sys.has_node("VEDO"):
		maxLv = 5
		
func loadInfo(id):
	.loadInfo(id)
	addAtt(attEx)
	if utils.getModNameFromData(self) == "原版" and lock != -1:
		if origin.find("谜团") > -1 or origin.find("祭坛") > -1:
			lock = 1
		else:
			lock = 0
			data.achUlock(self)
	
func delAtt(base):
	if atts.has(base) and base != attEx:
		atts.erase(base)
		if base.is_connected("onAttSet",self,"rAttSet"):
			base.disconnect("onAttSet",self,"rAttSet")
		upAtt(base)
	
#改写原版伤害函数
func hurtBase(atkInfo):
	if returnCast(): return atkInfo
	preHandleAtkInfo(atkInfo)
	if atkInfo.cha == null || atkInfo.cha.isDeath  || atkInfo.cha.isInvin: 
		return atkInfo
	
	if atkInfo.ignoreAll:
		return directHurt(atkInfo)

	atkInfo.castCha.emit_signal("onCastHurtStart",atkInfo)
	atkInfo.cha.emit_signal("onHurtStart",atkInfo)
	sys.game.emit_signal("onChaCastHurtStart",atkInfo)
	sys.game.emit_signal("onChaHurtStart",atkInfo)
	if atkInfo.virtual or atkInfo.val <= 0:return
	var defVal = atkInfo.cha.def if atkInfo.hurtType == HURTTYPE.PHY else atkInfo.cha.mdef
	defVal = clamp(defVal - atkInfo.castCha.pen, 0, 99999) * max(0, 1 - atkInfo.penL)
	if not atkInfo.isHoly:
		atkInfo.per = clamp((atkInfo.per - 1.0) * cons.perR + 1.0,0.05,10)
		atkInfo.finalPer = clamp((atkInfo.finalPer - 1.0) * cons.perR + 1.0,0.1,20)
		var totalPer = max(0.01, atkInfo.per * atkInfo.finalPer)
		if atkInfo.hurtType == HURTTYPE.REAL:
			atkInfo.finalVal = atkInfo.val * totalPer
		else:
			atkInfo.finalVal = atkInfo.val * ( 1 - defVal/(defVal+cons.defCoe) ) * totalPer
	if atkInfo.canCri && rndPer(cri) :
		atkInfo.isCri = true
	if atkInfo.isCri :
		atkInfo.finalVal *= criR

	atkInfo.cha.beforeHurt(atkInfo)
	atkInfo.cha.emit_signal("beforeHurt",atkInfo)
	if atkInfo.virtual or atkInfo.val <= 0:return

	var ward = atkInfo.cha.ward
	ward -= atkInfo.finalVal
	if ward < 0:
		if atkInfo.cha.hp + ward <= 0:
			atkInfo.cha.emit_signal("beforeDeathDmg", atkInfo)
			if atkInfo.virtual:
				return atkInfo
			atkInfo.finalVal += atkInfo.cha.hp + ward
		atkInfo.cha.hp += ward
		ward = 0
	if atkInfo.cha.hp <= 0:
		atkInfo.finalVal += atkInfo.cha.hp
		atkInfo.cha.hp = 0
		atkInfo.cha.death(atkInfo)
	atkInfo.cha.ward = ward
	afterHurt(atkInfo)
	return atkInfo

func beforeHurt(atkInfo):
	pass

#无视判定直接造成伤害
func directHurt(atkInfo):
	atkInfo.cha.hp -= atkInfo.finalVal
	if atkInfo.cha.hp <= 0:
		atkInfo.finalVal += atkInfo.cha.hp
		atkInfo.cha.hp = 0
		atkInfo.cha.ward = 0
		atkInfo.cha.death(atkInfo)
	afterHurt(atkInfo)
	return atkInfo

func afterHurt(atkInfo):
	atkInfo.castCha.emit_signal("onCastHurt",atkInfo)
	atkInfo.cha.emit_signal("onHurt",atkInfo)
	sys.game.emit_signal("onChaCastHurt",atkInfo)
	sys.game.emit_signal("onChaHurt",atkInfo)

func preHandleAtkInfo(atkInfo):
	#穿甲倍率，默认为0
	if !atkInfo.has("penL"):
		atkInfo["penL"] = 0
	#是否不产生实际伤害
	if !atkInfo.has("virtual"):
		atkInfo["virtual"] = false
	#是否神圣伤害，神圣伤害无视一切减伤
	if !atkInfo.has("isHoly"):
		atkInfo["isHoly"] = false
	#是否无视一切判定，直接造成伤害
	if !atkInfo.has("ignoreAll"):
		atkInfo["ignoreAll"] = false

#改写原版抽取随机技能函数
func upSKillsRnf(vari):
	#额外支持指定的技能池
	if vari.hasOrTab(tab) == false and not specSkills.has(vari.id): 
		return false
	for i in skills.items:
		if i.id == vari.id :
			return false
	for i in upSkills.items:
		if i.id == vari.id :
			return false
	return true

#获得单元格附近的友军
func getCellAlly(cell, ran = 1):
	var chas = []
	for i in sys.batScene.getCellChas(cell, 1):
		if i != null and i.team == self.team: 
			chas.append(i)
	return chas

#获得单元格附近的敌军
func getCellEnemy(cell, ran = 1):
	var chas = []
	for i in sys.batScene.getCellChas(cell, 1):
		if i != null and i.team != self.team: 
			chas.append(i)
	return chas

func _process(delta):
	._process(delta)
	if silenceTime > 0:
		silenceTime -= delta

#重写原版阵亡函数
func death(atkInfo):
	isDeath = true
	emit_signal("onDeathStart",atkInfo)
	atkInfo.castCha.emit_signal("onKillStart",atkInfo)
	sys.game.emit_signal("onChaDeathStart",atkInfo)
	sys.game.emit_signal("onChaKillStart",atkInfo)
	if isDeath:
		emit_signal("onDeath",atkInfo)
		atkInfo.castCha.emit_signal("onKill",atkInfo)
		sys.game.emit_signal("onChaDeath",atkInfo)
		sys.game.emit_signal("onChaKill",atkInfo)
		if atkInfo.castCha != self and atkInfo.castCha.get("killNum") != null:
			#增加杀敌数
			atkInfo.castCha.killNum += 1
		onDeath(atkInfo)	
		del()	

#亡语效果
func deathWord():
	for i in skills.items:
		if i.has_method("deathWord"):
			i.call("deathWord")

#死亡时执行此函数
func onDeath(atkInfo):
	deathWord()

#角色是否拥有该技能
func hasSkill(id):
	for i in skills.items:
		if i.id == id:
			return true
	return false

#获得角色指定技能
func getSkill(id):
	for i in skills.items:
		if i.id == id:
			return i
	return null


#需要自备atkInfo的伤害函数
func hurtWithInfo(atkInfo):
	if atkInfo.get("source") == null: 
		atkInfo["source"] = self
	if atkInfo.get("castCha") == null or atkInfo.get("cha") == null or atkInfo.get("val") == null: 
		return atkInfo
	if atkInfo.get("atkType") == null: 
		atkInfo["atkType"] = ATKTYPE.SKILL
	if atkInfo.get("hurtType") == null: 
		atkInfo["hurtType"] = HURTTYPE.PHY
	if atkInfo.get("per") == null: 
		atkInfo["per"] = 1.0
	if atkInfo.get("finalPer") == null: 
		atkInfo["finalPer"] = 1.0
	if atkInfo.get("finalVal") == null: 
		atkInfo["finalVal"] = 0
	if atkInfo.get("canCri") == null: 
		atkInfo["canCri"] = false
	if atkInfo.get("isCri") == null: 
		atkInfo["isCri"] = false
	return hurtBase(atkInfo)
	
#可以指定攻击目标的普攻
func normalAtk(cha = aiCha, playAnim = true, emitSg = true):
	if playAnim:
		playAnim("atk",spd)
		yield(ctime(0.3 / spd),"timeout")
	if ran > 1:
		var eff:Eff = sys.batScene.newEff("e_atk1",position,imgCenterPos)
		eff.get_node("up/img").frame = normEffInx
		eff.flyToChara(cha, projectileSpd)
		yield(ctime(0.1),"timeout")

	var atkInfo = {castCha=self,cha=cha,val=atk,source=self,atkType=ATKTYPE.NORMAL,hurtType=HURTTYPE.PHY,per=1.0,finalPer = 1.0,finalVal=0,canCri=true,isCri=false}
	if emitSg:
		emit_signal("onNormAtk")
		emit_signal("onNormalAtk", atkInfo)
	hurtBase(atkInfo)
		
#重写原版函数
func atkNorm():
	if (nowAnim == "idle" || nowAnim == "move") && aiCha != null && aiCha.isDeath == false && normAtkTime <= 0:
		normAtkTime = normAtkS / clamp(spd,0.001,100)
		_atkNorm()
		return true
	return false
#重写原版函数
func _atkNormR():
	atkNormR()

func atkNormR(cha = aiCha):
	var atkInfo = {castCha=self,cha=cha,val=atk,source=self,atkType=ATKTYPE.NORMAL,hurtType=HURTTYPE.PHY,per=1.0,finalPer = 1.0,finalVal=0,canCri=true,isCri=false}
	emit_signal("onNormAtk")
	emit_signal("onNormalAtk", atkInfo)
	hurtBase(atkInfo)
#重写原版函数
func _atkNorm():
	playAnim("atk",spd)
	yield(ctime(0.3 / spd),"timeout")
	if ran > 1:
		var eff:Eff = sys.batScene.newEff("e_atk1",position,imgCenterPos)
		eff.get_node("up/img").frame = normEffInx
		eff.flyToChara(aiCha, projectileSpd)
		eff.connect("onReach",self,"_atkNormR")
	else:
		_atkNormR()

#净化自身Buff
func dispelBuff(isDebuff = true):
	var tab = "deBuff" if isDebuff else "buff"
	for j in affs:
		if j.hasTab(tab):
			j.del()
			
#给予对应职业的随机装备（装备等级）
func addRndItem(lv):
	var rnd = rnp.eqpPool.rndItem(self,"addRndItemF")
	if rnd == null:return
	var item = data.newBase(rnd.id)
	item.create(lv)
	for i in range(eqps.items.size()):
		if eqps.items[i] == null :
			setEqp(item,i)

func addRndItemF(item):
	if item.hasOrTab(tab + " " + exTab):
		return true
	return false

func playerPlusLv():
	if skipSkillLvs.has(lv+1):
		sys.player.subItem("m_xp",maxXp)
		plusLv()
		return
	var dlg = sys.eventDlg.selSkill(upSkills)
	dlg.connect("onSel",self,"skillSel")
	dlg.reUpBtn.link(5,self,"upSkillsReady")
	
func aiPlusLv():
	plusLv()
	if not skipSkillLvs.has(lv):
		var skill = data.newBase(rnp.skillPool.rndItem(self,"rnfSkill").id)
		skills.addItem(skill)

func hasTab(tabs):
	if exTab != "":
		tabs += " " + exTab
	for i in tabs.split(" "):
		var b = false
		for j in tab.split(" "):
			if i == j :
				b = true
				continue
		if !b :return false
	return true

#召唤一个目标的克隆体(对象，位置，减伤等级)
func summCopy(ccha,cell=null,dlv = 0):
	if checkSummNum() >= maxSumm:return null
	if cell == null: 
		cell = self.cell
	var cha = data.newBase(ccha.id)
	cha.team = team
	cha.isSummon = true
	cha.summor = self
	cha.lv = lv
	cha.tab = ccha.tab
	for i in range(ccha.skills.items.size()):
		if i == 0 :continue
		var ok = ccha.skills.items[i]
		if skillIds.has(ok.id):continue
		var k = data.newBase(ok.id)
		k.lv = ok.lv
		cha.skills.addItem(k)
	for i in ccha.eqps.items:
		if i == null : continue
		var e = data.newBase(i.id)
		e.lv = i.lv
		for j in i.gems.items:
			var gem = data.newBase(j.id)
			gem.lv = j.lv
			e.gems.addItem(gem)
		cha.eqps.addItem(e)
	if cha != null && dlv > 0 :
		cha.castBuff(cha,"b_b_cuiRuo",dlv)

	if sys.batScene.addObj(cha,cell) :
		if is_instance_valid(cha.vis) :
			cha.vis.setProcess(sys.batScene.isBattle)
	sys.batScene.emit_signal("onCharaSumm", cha)		
	return cha

#召唤一个目标id的单位
func summ(id,cell = null):
	if checkSummNum() >= maxSumm:return null
	if cell == null: cell = self.cell
	var cha = data.newBase(id)
	cha.team = team
	cha.isSummon = true
	cha.summor = self
	cha.lv = lv
	sys.batScene.emit_signal("onCharaSumm", cha)	
	if sys.batScene.addObj(cha,cell) :
		if is_instance_valid(cha.vis) :
			cha.vis.setProcess(sys.batScene.isBattle)
	return cha

#召唤一个目标id的单位
func summon(id,cell = null,eqp = false):
	if checkSummNum() >= maxSumm:return null
	if cell == null: cell = self.cell
	var cha = data.newBase(id)
	cha.team = team
	cha.isSummon = true
	cha.summor = self
	if eqp:
		for i in self.eqps.items:
			if i == null : continue
			var e = data.newBase(i.id)
			e.lv = i.lv
			for j in i.gems.items:
				var gem = data.newBase(j.id)
				gem.lv = j.lv
				e.gems.addItem(gem)
			cha.eqps.addItem(e)
	sys.batScene.emit_signal("onCharaSumm", cha)	
	if sys.batScene.addObj(cha,cell) :
		if is_instance_valid(cha.vis) :
			cha.vis.setProcess(sys.batScene.isBattle)
	return cha		

func checkSummNum():
	if not sys.has_node("VEDO"):
		return 0
	var num = 0
	for i in sys.batScene.getAllAllys(self.team):
		if i.isSummon:
			num += 1
	return num

func aiPlusLvEx(tlv):
	for i in tlv - 1:
		aiPlusLv()
	if lv > 1:
		var kn = int(sp / skills.items.size())
		var ys = int(sp) % int(skills.items.size())
		for i in skills.items.size():
			var pn = kn + 1 if i < ys else kn
			skills.items[i].lv += pn
			sp -= pn

#强制杀死自己
func forceKillSelf():
	hp = 0
	ward = 0
	var atkInfo = {castCha=self,cha=self,val=1,source=null,atkType=ATKTYPE.EFF,hurtType=HURTTYPE.PHY,per=1.0,finalPer = 1.0,finalVal=0,canCri=true,isCri=false}
	death(atkInfo)

#返回主职业
func getOrientation():
	var t = tab + " " + exTab
	var s = t.split(" ")
	if s.size() > mainTabIndex:return s[mainTabIndex]
	else:return ""

#从角色身上移除该装备，装备会回到玩家装备池中
func delEqp(item):
	var inx = findEqpIndex(item)
	eqps.delInxItem(inx)
	item.wearer = null
	if team == sys.player.team and not item.get("once"):
		sys.player.items.addItem(item)

func findEqpIndex(eqp):
	for i in eqps.items.size():
		if eqps.items[i] == eqp:
			return i

func sortByMaxHp(a,b):
	return  a.maxHp < b.maxHp

func sortByMaxHpDesc(a,b):
	return  a.maxHp > b.maxHp

func sortByHp(a,b):
	return (!a.isSumm && b.isSumm && a.maxHp - a.hp > 50) || a.hp / a.maxHp < b.hp / b.maxHp

func sortByNowHp(a,b):
	return (!a.isSumm && b.isSumm && a.maxHp - a.hp > 50) || a.hp < b.hp

func sortByHpDesc(a,b):
	return (!a.isSumm && b.isSumm && a.maxHp - a.hp > 50) || a.hp / a.maxHp > b.hp / b.maxHp

func sortByAtkDesc(a,b):
	return a.atk > b.atk

func sortByMatkDesc(a,b):
	return a.matk > b.matk

func sortByAtk(a,b):
	return a.atk < b.atk

func sortByMatk(a,b):
	return a.matk < b.matk
	
func sortByLv(a,b):
	return a.lv < b.lv

func sortByLvDesc(a,b):
	return a.lv > b.lv

func sortByAllAtkDesc(a,b):
	return a.atk + a.matk > b.atk + b.matk

func sortByDef(a,b):
	return a.def < b.def

func sortByRan(a,b):
	return a.ran < b.ran

func sortByDefDesc(a,b):
	return a.def > b.def	


func getSave():
	var ds = {
		killNum = killNum,
		tab = tab,
		attEx = utils.toAttDs(attEx)
	}
	ds.merge(.getSave())
	return ds

func setSave(ds):
	.setSave(ds)
	dsSet("killNum",ds)
	dsSet("tab",ds)
#	dsSet("attEx",ds)
	if ds.has("attEx"):
		for i in ds.get("attEx").keys():
			attEx.set(i, ds.get("attEx").get(i))
	
func typeRotio(type = 1):
	attRatio(chaAttDs[type])
	
var chaAttDs = {
	1:{
		ran = 1,
		atk = 9,
		matk = 5,
		def = 5,
		mdef = 5,
		maxHp = 10,
	},
	"zs":{
		ran = 1,
		atk = 9,
		matk = 5,
		def = 5,
		mdef = 5,
		maxHp = 10,
	},
	2 : {
		ran = 4,
		atk = 9,
		matk = 5,
		def = 4,
		mdef = 4,
		maxHp = 7,
	},
	"ss" : {
		ran = 4,
		atk = 9,
		matk = 5,
		def = 4,
		mdef = 4,
		maxHp = 7,
	},
	3: {
		ran = 1,
		atk = 7,
		def = 5,
		matk = 5,
		mdef = 5,
		maxHp = 10,
	},
	"tk": {
		ran = 1,
		atk = 7,
		def = 5,
		matk = 5,
		mdef = 5,
		maxHp = 10,
	},
	4 : {
		ran = 1,
		atk = 8,
		matk = 5,
		def = 4,
		mdef = 4,
		maxHp = 9,
	},
	"ck" : {
		ran = 1,
		atk = 8,
		matk = 5,
		def = 4,
		mdef = 4,
		maxHp = 9,
	},
	5 : {
		ran = 3,
		atk = 6,
		def = 3,
		matk = 7,
		mdef = 4,
		maxHp = 6,
	},
	"fs" : {
		ran = 3,
		atk = 6,
		def = 3,
		matk = 7,
		mdef = 4,
		maxHp = 6,
	},
	6 :  {
		ran = 3,
		atk = 7,
		def = 4,
		matk = 7,
		mdef = 4,
		maxHp = 7,
	},
	"fz" :  {
		ran = 3,
		atk = 7,
		def = 4,
		matk = 7,
		mdef = 4,
		maxHp = 7,
	}
}

func setLv(val):
	.setLv(val)
	upAtt(attLv)

func setWard(val):
	ward = clamp(val, 0, maxHp * maxWardBouns)
	emit_signal("onSetWard")